package cs4game;

import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;

public class Camera {
	public float[] eye = new float[3]; //The (x,y,z) location of the camera
	public float[] center = new float[3]; //The focus of the camera (directly in front), represented as a point. Will be the player.
	public float[] up = new float[3], forward = new float[3], sideways = new float[3]; //Unit vectors, the moving trihedral of the camera
	public float degPitch, degYaw; //Rotation of the camera
	public MainStage stage;
	public float[] posChange = new float[]{0, 0, 0, 0}; //Compounds all movement instructions until they are physically performed, (x,y,z,num changes)

	public Camera(MainStage m){
		stage = m;
		eye = new float[]{1.25f, 1.3f, 1.25f}; //Places the camera at this (arbitrary) point//{1.25f, 1.3f, 1.25f}
		center = new float[]{1.25f, 1.3f, -0.75f}; //Focus the camera (in this case, directly behind);//{1.25f, 1.3f, -0.75f}
		up = new float[]{0.0f,1.0f,0.0f}; //Up is directly up
		degPitch = 0; //Aimed straight forward
		degYaw = -90; //Aimed backwards
		
		//Calculate the forward and sideways vectors based in the info above
		//forward();
		sideways();
		change();
		forward();
	}

	public Camera(MainStage m, float[] e, float[] c, float[] u){
		//Position values are entered as parameters
		eye = e;
		center = c;
		up = u;//normalize(u);
		
		//forward();
		sideways();
		change();
		forward();
	}

	public void move(float[] vector){
		//Given a 3D vector, move the camera (and its center of focus) by the amount given in the vector
		//X coordinate
		
		eye[0] += vector[0];
		center[0] += vector[0];
		//Y coordinate
		eye[1] += vector[1];
		center[1] += vector[1];
		//Z coordinate
		eye[2] += vector[2];
		center[2] += vector[2];
		
		/*BoundingBox box = boundingBox();
		for(Point3D p: stage.world.allBlocks.keySet()){
			
			int id = stage.world.allBlocks.get(p);
			BlockID def = stage.world.blockDefs.get(getID(id));

			if(def.getBoundingBox(p, id).intersects(box)){
				eye[0] -= vector[0];
				center[0] -= vector[0];
				eye[1] -= vector[1];
				center[1] -= vector[1];
				eye[2] -= vector[2];
				center[2] -= vector[2];
				box = boundingBox();
			}
			
		}*/

	}
	//public int getID(int block){return (block & 0xFFF00000)>>52;}

	/*public BoundingBox boundingBox() {
		return new BoundingBox(eye[0]-.25f, eye[0]+.25f, eye[1]-1.25f, eye[1]+.25f, eye[2]-.1f, eye[2]+.1f);
	}*/

	//Special cases of the move instructions
	public void moveX(float x){move(new float[]{x,0,0});}
	public void moveY(float y){move(new float[]{0,y,0});}
	public void moveZ(float z){move(new float[]{0,0,z});}

	//Moves the camera in the direction of its sideways vector
	public void moveSideways(float magnitude){
		float[] vector = new float[3];
		for(int i = 0; i<3; i++)
			vector[i]= -1*sideways[i];
		vector[1] = 0;
		vector = normalize(vector);
		vector[0] *= magnitude;		
		vector[2] *= magnitude;
		posChange[0] += vector[0];
		posChange[2] += vector[2];
		//posChange[3]++; 
	}

	public void moveForward(float magnitude){
		float[] vector = new float[3];
		vector[0] = (float) Math.cos(degYaw*Math.PI/180);
		vector[1] = 0;
		vector[2] = (float) Math.sin(degYaw*Math.PI/180);
		vector = normalize(vector);
		vector[0] *= magnitude;
		vector[2] *= magnitude;

		posChange[0] += vector[0];
		posChange[2] += vector[2];
		//posChange[3]++; 
	}

	public void moveUp(float magnitude){
		float[] vector = new float[3];
		//for(int i = 0; i<3; i++)
		//	vector[i]= up[i]*magnitude;
		vector = normalize(vector);
		move(vector);
	}


	public float[] forward(){
		forward = normalize(new float[]{center[0]-eye[0], center[1]-eye[1], center[2]-eye[2]});
		return forward;
	}

	public float[] sideways(){
		forward();
		sideways = normalize(crossProduct(up, forward));
		return sideways;
	}

	public float[] up(){
		forward();
		up = normalize(crossProduct(forward, sideways));
		return up;
	}

	public void centerOn(Vector3D p){
		//System.out.println(center[0]-p.x);
		move(new float[]{p.x-center[0], p.y-center[1], p.z-center[2]});
	}
	
	public void pitch(float degrees){
		if(degPitch+degrees <= 90 && degPitch+degrees >= -90){
			degPitch += degrees;
			forward[1] = (float) Math.sin(degPitch*Math.PI/180);
			forward[0] =(float) (Math.cos(degYaw*Math.PI/180)*(Math.cos(degPitch*Math.PI/180)));
			forward[2] =(float) (Math.sin(degYaw*Math.PI/180)*(Math.cos(degPitch*Math.PI/180)));

			forward = normalize(forward);
			center = new float[]{forward[0]+eye[0], forward[1]+eye[1], forward[2]+eye[2]};
			up();

		}
	}

	public void yaw(float degrees){
		degYaw += degrees;
		if(degYaw >360)//consider using modulus
			degYaw = degYaw-360;
		if(degYaw<0)
			degYaw = degYaw+360;
		
		float radYaw = (float) (degYaw*Math.PI/180);
		
		float magnitude = (float)Math.sqrt(forward[0]*forward[0] + forward[2]*forward[2]);
		forward[0] = (float) Math.cos(radYaw)*magnitude*5;
		forward[2] = (float) Math.sin(radYaw)*magnitude*5;

		if(degPitch>=0){
			up[0] = (float) (-1*Math.cos(radYaw)*Math.cos((90-degPitch)*Math.PI/180));
			up[1] = (float) (Math.sin((90-degPitch)*Math.PI/180));
			up[2] = (float) (-1*Math.sin(radYaw)*Math.cos((90-degPitch)*Math.PI/180));
		}
		else{
			up[0] = (float) (Math.cos(radYaw)*Math.cos((-90-degPitch)*Math.PI/180));
			up[1] = (float) (-1*Math.sin((-90-degPitch)*Math.PI/180));
			up[2] = (float) (Math.sin(radYaw)*Math.cos((-90-degPitch)*Math.PI/180));
		}
		center = new float[]{forward[0]+eye[0], forward[1]+eye[1], forward[2]+eye[2]};
		//this.roll(180);
		sideways();
	}

	public void roll(float degrees){
		up[0] += Math.sin(degrees*Math.PI/180)*(up[0]-sideways[0]);
		up[1] += Math.sin(degrees*Math.PI/180)*(up[1]-sideways[1]);
		up[2] += Math.sin(degrees*Math.PI/180)*(up[2]-sideways[2]);
		up = normalize(up);
		sideways();
	}

	//Resets the camera based on new position and direction values
	public void change(){
		if(posChange[3] != 0){
			//move(new float[]{posChange[0]/posChange[3], 0, 0});
			//move(new float[]{0, 0, posChange[2]/posChange[3]});
			//posChange = new float[]{0, 0, 0, 0};
		}


		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glLoadIdentity();
		GLU.gluLookAt(eye[0], eye[1], eye[2], center[0], center[1], center[2], up[0], up[1], up[2]);
	}

	//Vector Transformation Functions:

	public float[] normalize(float[] array){
		float norm = (float) Math.sqrt(array[0]*array[0] + array[1]*array[1] + array[2]*array[2]);
		float[] newArray = new float[3];
		newArray[0] = array[0]/ norm;
		newArray[1] = array[1]/ norm;
		newArray[2] = array[2]/ norm;

		return newArray;
	}

	public float[] crossProduct(float[] vector1, float[] vector2){
		float[] cross = new float[3];
		cross[0] = vector1[1] * vector2[2] - vector1[2]*vector2[1];
		cross[1] = vector1[2] * vector2[0] - vector1[0]*vector2[2];
		cross[2] = vector1[0] * vector2[1] - vector1[1]*vector2[0];
		//System.out.println("cross ("+cross[0]+", "+cross[1]+", "+cross[2]+")");
		return cross;
	}

	

	public void printVector(float[] vector){
		System.out.println("("+vector[0]+", "+vector[1]+", "+vector[2]+")");
	}


}

